/*
 * module_uart.c
 *
 *  Created on: Jun 12, 2010
 *      Author: jmonk
 */

#include "module_uart.h"
#include "mox.h"
#include "mox_module.h"
#include "mox_threads.h"
#include <xccompat.h>
#include <xs1_g4000b-512.h>
#include <print.h>


inline chanend create_chan_remote(chanend chan_mox, int first)
{
	register int tmp;
	register int tmp2;

	asm("getr %0,0x2":"=r"(tmp):/*no inputs*/);
	if (first) {
		mox_chan_out_wd(chan_mox,tmp);
		tmp2 = mox_chan_in_wd(chan_mox);
	} else {
		tmp2 = mox_chan_in_wd(chan_mox);
		mox_chan_out_wd(chan_mox,tmp);
	}
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp), "r"(tmp2));


	return tmp;
}

inline void free_chanend(chanend a)
{
	asm("freer res[%0]":/*no outputs*/:"r"(a));
	return;
}

inline void create_chan_local(chanend *a, chanend *b)
{
	register int tmp;
	register int tmp2;

	asm("getr %0,0x2":"=r"(tmp):/*no inputs*/);
	asm("getr %0,0x2":"=r"(tmp2):/*no inputs*/);
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp), "r"(tmp2));
	asm("setd res[%0],%1":/*no outputs*/:"r"(tmp2), "r"(tmp));

	*a = tmp;
	*b = tmp2;
	return;
}

void module_start_uart_0(void)
{
	chanend mox;
	chanend rxc, txc;
	chanend rxc1, txc1;
	port rx, tx;
	unsigned thd_id;
	unsigned info[] = { TH_TYPE_MOD, MOD_GEN, 1};
	mox_msg thd_info = {
			TH_PARAM,
			3*sizeof(unsigned),
			(unsigned char *)info
	};

	THREAD_START_GET_CHAN(mox);

	thd_id = thd_std_start(mox, thd_info);

	rx = XS1_PORT_1I;
	tx = XS1_PORT_1H;

	asm("setc res[%0],0x08"::"r"(rx));
	asm("setc res[%0],0x08"::"r"(tx));

	create_chan_local(&rxc, &rxc1);
	create_chan_local(&txc, &txc1);
	uart_module_start(mox, rxc, rxc1, txc, txc1, rx, tx);
}

inline void mox_handler(chanend mox, chanend rxc, chanend txc)
{
	unsigned tmp;
	chanend in = 0;
	chanend out = 0;
	mox_msg *msg;

	while (1) {
		msg = thd_comm_msg_recv_nblock(mox);
		if (msg && ((msg->type & MSG_TYPE) == MOD_MSG)) {
			msg->type = MOD_READY;
			thd_comm_msg_send(mox, *msg);
		} else if (msg && ((msg->type & MSG_TYPE) == SIG_MSG)) {
			switch (msg->type) {
			case SIG_ICONN:
				if (!in) {
					msg->type = SIG_ACK;
					in = mox_chan_in_wd(mox);
				} else {
					msg->type = SIG_NACK;
				}
				break;
			case SIG_OCONN:
				if (!out) {
					msg->type = SIG_ACK;
					out = mox_chan_in_wd(mox);
				} else {
					msg->type = SIG_NACK;
				}
				break;
			case SIG_IDISC:
				if (in) {
					msg->type = SIG_ACK;
					in = 0;
				} else {
					msg->type = SIG_NACK;
				}
				break;
			case SIG_ODISC:
				if (out) {
					msg->type = SIG_ACK;
					out = 0;
				} else {
					msg->type = SIG_NACK;
				}
				break;
			case SIG_IFREE:
				if (!in) {
					msg->type = SIG_ACK;
				} else {
					msg->type = SIG_NACK;
				}
				break;
			case SIG_OFREE:
				if (!out) {
					msg->type = SIG_ACK;
				} else {
					msg->type = SIG_NACK;
				}
				break;
			default:
				printhexln(tmp);
				msg->type = SIG_NONE;
				break;
			}
			thd_comm_msg_send(mox, *msg);
		}
		if (in) {
			tmp = chan_in_noblock(in);
			if (tmp != -1) {
				mox_chan_out_wd(txc, tmp);
			}
		}
		tmp = chan_in_noblock(rxc);
		if ((tmp != -1) && out) {
			mox_chan_out_wd(out, tmp);
		}
	}
}
